package com.jinyang.baserouter.utils

import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Build
import android.os.Bundle
import java.lang.NullPointerException
import java.lang.reflect.InvocationTargetException
import java.util.*

/**
 * @Author:         LiuJinYang
 * @CreateDate:     2021/11/25
 * @Description:
 */
class ApplicationUtil private constructor() {

    companion object {
        val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { ApplicationUtil() }
    }


    var isLogin: Boolean = false
    private var mContext: Context? = null
    private var mApplication: Application? = null
    private val mActivityLifecycleImpl: ActivityLifecycleImpl = ActivityLifecycleImpl()

    fun init(application: Application?) {
        mApplication = application ?: getApplicationByReflect()
        if (mContext == null) {
            mContext = application?.applicationContext
        }
        mApplication?.registerActivityLifecycleCallbacks(mActivityLifecycleImpl)
    }

    private fun getApplicationByReflect(): Application {
        try {
            @SuppressLint("PrivateApi") val activityThread =
                Class.forName("android.app.ActivityThread")
            val thread = activityThread.getMethod("currentActivityThread").invoke(null)
            val app = activityThread.getMethod("getApplication").invoke(thread)
                ?: throw NullPointerException("u should init first")
            return app as Application
        } catch (e: NoSuchMethodException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        } catch (e: InvocationTargetException) {
            e.printStackTrace()
        } catch (e: ClassNotFoundException) {
            e.printStackTrace()
        }
        throw NullPointerException("u should init first")
    }

    fun getAppContext(): Context? {
        return mContext
    }

    fun getTopActivity(): Activity? {
        return mActivityLifecycleImpl.getTopActivity()
    }

}

interface OnAppStatusChangedListener {
    fun onForeground()
    fun onBackground()
}

interface OnActivityDestroyedListener {
    fun onActivityDestroyed(activity: Activity?)
}

class ActivityLifecycleImpl : Application.ActivityLifecycleCallbacks {
    val mActivityList = LinkedList<Activity>()
    val mStatusListenerMap: MutableMap<Any, OnAppStatusChangedListener> =
        HashMap<Any, OnAppStatusChangedListener>()
    val mDestroyedListenerMap: MutableMap<Activity, MutableSet<OnActivityDestroyedListener?>> =
        HashMap<Activity, MutableSet<OnActivityDestroyedListener?>>()
    private var mForegroundCount = 0
    private var mConfigCount = 0
    private var mIsBackground = false
    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        setTopActivity(activity)
    }

    override fun onActivityStarted(activity: Activity) {
        if (!mIsBackground) {
            setTopActivity(activity)
        }
        if (mConfigCount < 0) {
            ++mConfigCount
        } else {
            ++mForegroundCount
        }
    }

    override fun onActivityResumed(activity: Activity) {
        setTopActivity(activity)
        if (mIsBackground) {
            mIsBackground = false
            postStatus(true)
        }
    }

    override fun onActivityPaused(activity: Activity) {}
    override fun onActivityStopped(activity: Activity) {
        if (activity.isChangingConfigurations) {
            --mConfigCount
        } else {
            --mForegroundCount
            if (mForegroundCount <= 0) {
                mIsBackground = true
                postStatus(false)
            }
        }
    }

    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { /**/
    }

    override fun onActivityDestroyed(activity: Activity) {
        mActivityList.remove(activity)
        consumeOnActivityDestroyedListener(activity)
    }

    fun getTopActivity(): Activity? {
        if (!mActivityList.isEmpty()) {
            for (i in mActivityList.indices.reversed()) {
                val activity = mActivityList[i]
                if (activity == null || activity.isFinishing
                    || Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed
                ) {
                    continue
                }
                return activity
            }
        }
        val topActivityByReflect: Activity? = getTopActivityByReflect()
        if (topActivityByReflect != null) {
            setTopActivity(topActivityByReflect)
        }
        return topActivityByReflect
    }
    fun addOnAppStatusChangedListener(
        `object`: Any,
        listener: OnAppStatusChangedListener
    ) {
        mStatusListenerMap[`object`] = listener
    }

    fun removeOnAppStatusChangedListener(`object`: Any) {
        mStatusListenerMap.remove(`object`)
    }

    fun removeOnActivityDestroyedListener(activity: Activity?) {
        if (activity == null) return
        mDestroyedListenerMap.remove(activity)
    }

    fun addOnActivityDestroyedListener(
        activity: Activity?,
        listener: OnActivityDestroyedListener?
    ) {
        if (activity == null || listener == null) return
        val listeners: MutableSet<OnActivityDestroyedListener?>
        if (!mDestroyedListenerMap.containsKey(activity)) {
            listeners = HashSet<OnActivityDestroyedListener?>()
            mDestroyedListenerMap[activity] = listeners
        } else {
            listeners = mDestroyedListenerMap[activity]!!
            if (listeners.contains(listener)) return
        }
        listeners.add(listener)
    }


    private fun postStatus(isForeground: Boolean) {
        if (mStatusListenerMap.isEmpty()) return
        for (onAppStatusChangedListener in mStatusListenerMap.values) {
            if (onAppStatusChangedListener == null) return
            if (isForeground) {
                onAppStatusChangedListener.onForeground()
            } else {
                onAppStatusChangedListener.onBackground()
            }
        }
    }

    private fun setTopActivity(activity: Activity) {
        if (mActivityList.contains(activity)) {
            if (mActivityList.last != activity) {
                mActivityList.remove(activity)
                mActivityList.addLast(activity)
            }
        } else {
            mActivityList.addLast(activity)
        }
    }

    private fun consumeOnActivityDestroyedListener(activity: Activity) {
        val iterator: MutableIterator<Map.Entry<Activity, Set<OnActivityDestroyedListener?>>> =
            mDestroyedListenerMap.entries.iterator()
        while (iterator.hasNext()) {
            val entry: Map.Entry<Activity, Set<OnActivityDestroyedListener?>> = iterator.next()
            if (entry.key === activity) {
                val value: Set<OnActivityDestroyedListener?> = entry.value
                for (listener in value) {
                    listener?.onActivityDestroyed(activity)
                }
                iterator.remove()
            }
        }
    }

    private fun getTopActivityByReflect(): Activity? {
        try {
            @SuppressLint("PrivateApi") val activityThreadClass =
                Class.forName("android.app.ActivityThread")
            val currentActivityThreadMethod =
                activityThreadClass.getMethod("currentActivityThread").invoke(null)
            val mActivityListField = activityThreadClass.getDeclaredField("mActivityList")
            mActivityListField.isAccessible = true
            val activities = mActivityListField[currentActivityThreadMethod] as Map<*, *>
                ?: return null
            for (activityRecord in activities.values) {
                val activityRecordClass: Class<*> = activityRecord!!.javaClass
                val pausedField = activityRecordClass.getDeclaredField("paused")
                pausedField.isAccessible = true
                if (!pausedField.getBoolean(activityRecord)) {
                    val activityField = activityRecordClass.getDeclaredField("activity")
                    activityField.isAccessible = true
                    return activityField[activityRecord] as Activity
                }
            }
        } catch (e: ClassNotFoundException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        } catch (e: InvocationTargetException) {
            e.printStackTrace()
        } catch (e: NoSuchMethodException) {
            e.printStackTrace()
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        }
        return null
    }
}
